from abc import ABC, abstractmethod
import os
import re
import aiohttp
from typing import Dict, List, Optional, Tuple, Callable

class BaseDownloader(ABC):
    def __init__(self, config):
        self.config = config
        self.headers = config.get_headers()
        self.session = None
    
    async def _get_session(self) -> aiohttp.ClientSession:
        """获取或创建 aiohttp 会话"""
        if self.session is None or self.session.closed:
            self.session = aiohttp.ClientSession(headers=self.headers)
        return self.session
    
    @abstractmethod
    async def extract_video_info(self, url: str) -> Dict:
        """提取视频信息"""
        pass
    
    @abstractmethod
    async def download_video(self, url: str, format_id: str, progress_callback: Optional[Callable] = None) -> Tuple[bool, str]:
        """下载视频"""
        pass
    
    def _clean_filename(self, title: str) -> str:
        """清理文件名"""
        return re.sub(r'[\\/:*?"<>|]', '_', title)
    
    def _get_save_path(self, title: str, ext: str) -> str:
        """获取保存路径"""
        safe_title = self._clean_filename(title)
        return os.path.join(self.config.download_dir, f"{safe_title}.{ext}")
    
    async def _download_file(self, url: str, save_path: str, progress_callback: Optional[Callable] = None) -> Tuple[bool, str]:
        """通用文件下载方法"""
        try:
            session = await self._get_session()
            async with session.get(url) as response:
                if response.status != 200:
                    return False, f"下载失败: HTTP {response.status}"
                
                # 获取文件大小
                total_size = int(response.headers.get('content-length', 0))
                block_size = 1024  # 1 KB
                downloaded = 0
                
                # 开始下载
                with open(save_path, 'wb') as f:
                    async for chunk in response.content.iter_chunked(block_size):
                        downloaded += len(chunk)
                        f.write(chunk)
                        if progress_callback and total_size:
                            progress = downloaded / total_size
                            if not progress_callback(progress):
                                # 如果回调返回False，表示取消下载
                                return False, "下载已取消"
            
            return True, save_path
            
        except Exception as e:
            print(f"下载文件时出错: {str(e)}")
            return False, f"下载失败: {str(e)}"
        finally:
            if self.session and not self.session.closed:
                await self.session.close()
    
    def _format_video_info(self, title: str, formats: List[Dict], cover_url: str) -> Dict:
        """格式化视频信息"""
        return {
            "title": title,
            "formats": formats,
            "cover_url": cover_url
        } 